home *** CD-ROM | disk | FTP | other *** search
/ Light ROM 1 / LIGHT-ROM 1 (Amiga Library Services)(1994).iso / ffdisks / d879.lha / DiskTest / Source / dt.c < prev    next >
C/C++ Source or Header  |  1993-04-06  |  20KB  |  776 lines

  1. /*-----------------------------------------*
  2.  | File: DT.c - Main disk testing routines |
  3.  +-----------------------------------------+---------------*
  4.  | Author:  Maurizio Loreti, aka MLO or I3NOO.             |
  5.  | Address: University of Padova - Department of Physics   |
  6.  |          Via F. Marzolo, 8 - 35131 PADOVA - Italy       |
  7.  | Phone:   (39)(49) 844-313         FAX: (39)(49) 844-245 |
  8.  | E-Mail:  loreti@padova.infn.it (TCP/IP)                 |
  9.  | Home: Via G. Donizetti 6 - 35010 CADONEGHE (PD) - Italy |
  10.  *---------------------------------------------------------*/
  11.  
  12. /**
  13.  | #includes
  14. **/
  15.  
  16. #include <stddef.h>                       /* Standard library */
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <stdarg.h>
  20. #include <string.h>
  21. #include <exec/types.h>                   /* Amiga specific */
  22. #include <exec/memory.h>
  23. #include <devices/trackdisk.h>
  24. #include <workbench/startup.h>
  25. #include <clib/exec_protos.h>
  26. #include <clib/gadtools_protos.h>
  27. #include <clib/asl_protos.h>
  28. #include <clib/intuition_protos.h>
  29. #include <clib/alib_protos.h>
  30. #include <clib/dos_protos.h>
  31. #include <clib/wb_protos.h>
  32. #include "main.h"
  33. #include "dt.h"                           /* Local stuff */
  34. #include "ext.h"
  35.  
  36. void EventLoop(void)
  37. {
  38. /**
  39.  | Main event loop.
  40.  | First, we initialise the AppWindow environment; then we Wait()
  41.  | for a signal from IDCMP or the AppWindow, and take the appropriate
  42.  | action.
  43. **/
  44.  
  45.   ULONG signalSet;
  46.  
  47.   if ((appWinPort = CreateMsgPort()) == NULL) {
  48.     Error("Error from CreateMsgPort");
  49.   }
  50.  
  51.   if ((pAWind = AddAppWindow(AW_ID, 0, pWind, appWinPort,
  52.                              TAG_END)) == NULL) {
  53.     Error("Error from AddAppWindow");
  54.   }
  55.  
  56.   signalSet =
  57.     (1L << appWinPort->mp_SigBit)  |  (1L << pWind->UserPort->mp_SigBit);
  58.  
  59.   FOREVER {
  60.     struct IntuiMessage *pIM;
  61.     struct AppMessage *pAM;
  62.  
  63.     (void) Wait(signalSet);
  64.  
  65.     while ((pIM = GT_GetIMsg(pWind->UserPort)) != NULL) {
  66.       ULONG class;
  67.       UWORD code;
  68.       struct Gadget *pG;
  69.  
  70.       class     = pIM->Class;
  71.       code      = pIM->Code;
  72.       pG        = (struct Gadget *) pIM->IAddress;
  73.       GT_ReplyIMsg(pIM);
  74.  
  75.       switch (class) {
  76.         case GADGETDOWN:
  77.         case GADGETUP:
  78.           switch (pG->GadgetID) {
  79.             case BUT_QUIT:
  80.               Cleanup();
  81.               break;
  82.             case BUT_1:
  83.             case BUT_2:
  84.             case BUT_3:
  85.             case BUT_4:
  86.               StartTest(0);
  87.               DiskCheck(pG->GadgetID);
  88.               EndTest();
  89.               break;
  90.             case BUT_FN:
  91. /* NO see RKM listFilenames = (BOOL)(pG->Flags & GFLG_SELECTED; */
  92.               listFileNames = (BOOL)(!listFileNames);
  93.               break;
  94.             case SCROLLER:
  95.               SetTopLine(code);
  96.               break;
  97.             default:
  98.               break;
  99.           }
  100.           break;
  101.         case MOUSEMOVE:
  102.           SetTopLine(code);
  103.           break;
  104.         case REFRESHWINDOW:
  105.           GT_BeginRefresh(pWind);
  106.           RefreshView(TRUE);
  107.             GT_EndRefresh(pWind, TRUE);
  108.           break;
  109.         case MENUPICK:
  110.           while (code != MENUNULL) {
  111.             struct MenuItem *pMI;
  112.             int menuFlag;
  113.  
  114.             pMI = ItemAddress(pMenu, code);
  115.             menuFlag = (int) GTMENUITEM_USERDATA(pMI);
  116.             ActionMenu(menuFlag);
  117.             code = pMI->NextSelect;
  118.           }
  119.           break;
  120.         default:
  121.           break;
  122.       }
  123.     }
  124.  
  125.     while ((pAM = (struct AppMessage *) GetMsg(appWinPort)) != NULL) {
  126.       int i;
  127.       BPTR dirLock;
  128.       struct WBArg *pWBA;
  129.       char dirName[PATHNAME_MAX];
  130.       char filName[FILENAME_MAX];
  131.  
  132.       for (i=0, pWBA=pAM->am_ArgList; i<pAM->am_NumArgs; i++, pWBA++) {
  133.         if (i) AddLine("", 0, TRUE);
  134.         strcpy(filName, pWBA->wa_Name);
  135.         if (filName[0]) {
  136.           dirLock = DupLock(pWBA->wa_Lock);
  137.         } else {
  138.           (void) NameFromLock(pWBA->wa_Lock,
  139.                               dirName, PATHNAME_MAX);
  140.         }
  141.  
  142.         if (filName[0]) {
  143.           FileStuff(dirLock, NULL, filName, i);
  144.         } else {
  145.           DirStuff(dirName, i);
  146.         }
  147.       }
  148.       ReplyMsg((struct Message *) pAM);
  149.     }
  150.   }
  151. }
  152.  
  153. /*-------------------------- Local Procedures --------------------------*/
  154.  
  155. static void ActionMenu(
  156.   const int code
  157. ){
  158. /**
  159.  | This procedure handles all the requests made using a menu
  160. **/
  161.  
  162.   switch (code) {
  163.     case M_ABOUT:
  164.       {
  165.         struct EasyStruct aboutES = {
  166.           sizeof(struct EasyStruct), 0,
  167.           PROG_NAME " message:",
  168.           "DiskTest -- %s\nAuthor: Maurizio Loreti (MLO)\n"
  169.                           "EMail:  loreti@padova.infn.it",
  170.           "OK"
  171.         };
  172.  
  173.         (void) EasyRequest(pWind, &aboutES, NULL, VersionTag+7);
  174.       }
  175.       break;
  176.  
  177.     case M_QUIT:
  178.       Cleanup();
  179.       break;
  180.  
  181.     case M_DF0:
  182.     case M_DF1:
  183.     case M_DF2:
  184.     case M_DF3:
  185.       StartTest(0);
  186.       DiskCheck(code);
  187.       EndTest();
  188.       break;
  189.  
  190.     case M_DEVDIR:
  191.       if (AslRequestTags(pFR, ASL_ExtFlags1, FIL1F_NOFILES, TAG_END)) {
  192.         DirStuff(pFR->rf_Dir, 0);
  193.       }
  194.       break;
  195.  
  196.     case M_FILE:
  197.       if (AslRequestTags(pFR, ASL_ExtFlags1, 0, TAG_END)) {
  198.         int i;
  199.         BPTR dLock;
  200.         struct WBArg *pWBA;
  201.  
  202.         if (dLock = Lock(pFR->rf_Dir, ACCESS_READ)) {
  203.           for (i=0, pWBA=pFR->rf_ArgList; i<pFR->rf_NumArgs; i++, pWBA++) {
  204.             if (i) AddLine("", 0, TRUE);
  205.             FileStuff(dLock, pFR->rf_Dir, pWBA->wa_Name, i);
  206.           }
  207.           UnLock(dLock);
  208.         }
  209.       }
  210.       break;
  211.  
  212.     default:
  213.       break;
  214.   }
  215. }
  216.  
  217. static unsigned CheckBreak(void)
  218. {
  219. /**
  220.  | Called from time to time to check for user breaks; checks the
  221.  | CTRL-C signal (actually unused) and the "break window" gadgets.
  222.  | The returned value is the "abortDT" global variable itself.
  223. **/
  224.  
  225.   if (!abortDT  &&
  226.       (SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C))
  227.           abortDT |= BRK_DETECTED;
  228.  
  229.   if (pBWind != NULL) {
  230.     struct IntuiMessage *pIM;
  231.  
  232.     if ((pIM = GT_GetIMsg(pBWind->UserPort)) != NULL) {
  233.       GT_ReplyIMsg(pIM);
  234.       abortDT |= BRK_DETECTED;
  235.     }
  236.   }
  237.  
  238.   if (abortDT  &&  !(abortDT & WARN_PRINTED)) {
  239.     OutLine(FALSE, "*** DiskTest: BREAK ***");
  240.     abortDT |= WARN_PRINTED;
  241.   }
  242.  
  243.   return abortDT;
  244. }
  245.  
  246. static void CheckDir(
  247.   char *path,
  248.   const BOOL root
  249. ){
  250. /**
  251.  | This procedure checks (recursively) a directory.
  252.  | "path" contains the full directory name, and "root" is non-zero if
  253.  | this directory is at the top level (the difference is in the special
  254.  | handling of the ':', and in the replacement of the device name (e.g.
  255.  | "DH0:") with its label (e.g. "Workbench:"), in general more meaningful.
  256.  |
  257.  | CheckDir() scans the wanted directory, checking immediately the 'true'
  258.  | files; the subdirectories (if any) are checked recursively at the
  259.  | end, one by one.
  260.  | If an error is detected from CheckDir(), the directory/file test is
  261.  | stopped and the global flag "abortDT" is set; this makes possible, in
  262.  | the further steps, to recursively free() all the memory that has been
  263.  | allocated in order to store the subdirectory names.
  264.  |
  265.  | First: obtain a lock on the wanted directory, and Examine() the lock;
  266.  | since only one directory is being scanned at a time, we can use a
  267.  | single FileInfoBlock global buffer.
  268. **/
  269.  
  270.   dirEntry *rdE = NULL;
  271.   BPTR dlock;
  272.  
  273.   if ((dlock = Lock(path, ACCESS_READ)) == 0) {
  274.     OutLine(FALSE, "* Can't access directory %s !", path);
  275.     abortDT = INTERNAL_ERR;
  276.     nErFil++;
  277.   } else {
  278.     if (!Examine(dlock, pFIB)) {
  279.       OutLine(FALSE,
  280.               "* Error return from Examine(), directory %s", path);
  281.       abortDT = INTERNAL_ERR;
  282.       nErFil++;
  283.     } else {
  284.  
  285. /**
  286.  | Prepare in "fileName" the full directory name - to which local
  287.  | filenames will be appended.
  288. **/
  289.  
  290.       char fileName[PATHNAME_MAX + FILENAME_MAX];
  291.       char *pc;
  292.  
  293.       if (root) {
  294.         pc = strcpy(fileName, pFIB->fib_FileName);
  295.         pc += strlen(fileName);
  296.         *pc++ = ':';
  297.         *pc = '\0';
  298.         OutLine(FALSE,
  299.                 "  checking files in root directory %s ...", fileName);
  300.       } else {
  301.         pc = strcpy(fileName, path);
  302.         pc += strlen(fileName);
  303.         OutLine(FALSE,
  304.                 "  checking files in directory %s ...", fileName);
  305.         *pc++ = '/';
  306.       }
  307.  
  308.       nDirs++;
  309.  
  310. /**
  311.  | Now, loop over all directory entries. As already said, all the
  312.  | 'real' files are immediately checked; the subdirectory names are
  313.  | stored in a linked list to be examined at the end. This list is
  314.  | implemented as a LIFO tree (the simplest type).
  315. **/
  316.  
  317.       while (ExNext(dlock, pFIB)) {
  318.         (void) strcpy(pc, pFIB->fib_FileName);
  319.         if (pFIB->fib_DirEntryType < 0) {
  320.           CheckFile(fileName);
  321.         } else {
  322.  
  323. /**
  324.  | If a memory allocation error is detected when asking space for
  325.  | our linked list, we exit the "while" loop; setting the "abortDT"
  326.  | flag before exiting ensures that all the memory we had from
  327.  | these malloc()'s will later be free()-ed recursively.
  328. **/
  329.  
  330.           dirEntry *pdE;
  331.  
  332.           if ((pdE = malloc(sizeof(dirEntry) + strlen(fileName))) == NULL) {
  333.             OutLine(FALSE, "* Can't allocate heap memory!");
  334.             abortDT = INTERNAL_ERR;
  335.             break;
  336.           }
  337.  
  338.           (void) strcpy(pdE->name, fileName);
  339.           pdE->next = rdE;
  340.           rdE = pdE;
  341.         }
  342.  
  343.         if (CheckBreak()) break;
  344.       }
  345.  
  346. /**
  347.  | We now check if ExNext() has failed, or if the last
  348.  | directory entry has been found.
  349. **/
  350.  
  351.       if (!abortDT) {
  352.         int errno;
  353.  
  354.         if ((errno = IoErr()) != ERROR_NO_MORE_ENTRIES) {
  355.           OutLine(FALSE, "* Error %d reading directory %s !",
  356.                   errno, path);
  357.           nErFil++;
  358.         }
  359.       }
  360.     }
  361.     UnLock(dlock);
  362.   }
  363.  
  364. /**
  365.  | Now, we loop over all detected subdirectories (if any);
  366.  | freeing in the same time the memory used to store their names.
  367. **/
  368.  
  369.   while (rdE != NULL) {
  370.     dirEntry *pdE;
  371.  
  372.     if (!abortDT) CheckDir(rdE->name, FALSE);
  373.  
  374.     pdE = rdE->next;
  375.     free(rdE);
  376.     rdE = pdE;
  377.   }
  378. }
  379.  
  380. static void CheckFile(
  381.   char *name
  382. ){
  383. /**
  384.  | Check a file, opening and reading it record by record.
  385. **/
  386.  
  387.   BPTR pFH;
  388.  
  389.   nFiles++;
  390.   if (listFileNames) OutLine(FALSE, "    file %s ...", name);
  391.  
  392.   if ((pFH = Open(name, MODE_OLDFILE)) == 0) {
  393.     OutLine(FALSE, "* Error %d opening file \"%s\".", IoErr(), name);
  394.     nErFil++;
  395.   } else {
  396.     long ier;
  397.  
  398.     while (!abortDT  &&
  399.            (ier = Read(pFH, diskBuffer, (long) diskBufferLength)) > 0) {
  400.       (void) CheckBreak();
  401.     }
  402.     (void) Close(pFH);
  403.  
  404.     if (ier < 0) {
  405.       OutLine(FALSE, "* Error %d reading file \"%s\".", IoErr(), name);
  406.       nErFil++;
  407.     }
  408.   }
  409. }
  410.  
  411. static void DirStuff(
  412.   char *name,
  413.   int i
  414. ){
  415. /**
  416.  | The driver for a device/directory integrity test.
  417.  | We allocate a buffer and clear the scroller, then
  418.  | call CheckDir in order to perform the actual test.
  419.  | If i is zero, the scroller is cleared.
  420. **/
  421.  
  422.   char *pc;
  423.  
  424.   GetBuffer(FILBUF_DIM);
  425.   StartTest(i);
  426.   OutLine(FALSE, "File integrity check ...");
  427.   pc = name + strlen(name) - 1;
  428.   CheckDir(name, (BOOL)(*pc == ':'));
  429.   EndTest();
  430. }
  431.  
  432. static void DiskCheck(
  433.   const int drive
  434. ){
  435. /**
  436.  | Full test for the floppy unit "drive".
  437.  | Properly opened the device, we check for a disk in drive and
  438.  | obtain the floppy disk parameters from various status commands;
  439.  | then we allocate the memory for our internal buffers.
  440. **/
  441.  
  442.   char driveName[] = "DF0:";
  443.   int error;
  444.   int cyl;
  445.   int head;
  446.   ULONG total;
  447.  
  448.   if ((diskPort = CreatePort(NULL, 0L)) == NULL) {
  449.     Error("Error from CreatePort");
  450.   }
  451.  
  452.   if ((diskReq = (struct IOExtTD *)
  453.       CreateExtIO(diskPort, sizeof(struct IOExtTD))) == NULL) {
  454.     Error("Error from CreateExtIo");
  455.   }
  456.  
  457.   driveName[2] += (char) drive;
  458.  
  459.   if ((error =
  460.       OpenDevice(TD_NAME, (ULONG) drive,
  461.                  (struct IORequest *) diskReq, 0L)) != 0) {
  462.     sprintf(slate, "Error 0x%X from OpenDevice", error);
  463.     Error(slate);
  464.   }
  465.  
  466.   diskReq->iotd_Req.io_Command = TD_CHANGESTATE;
  467.   (void) DoIO((struct IORequest *) diskReq);
  468.   if (diskReq->iotd_Req.io_Actual) {
  469.     OutLine(FALSE, "No disk in drive %d ...", drive);
  470.     return;
  471.   }
  472.  
  473.   diskReq->iotd_Req.io_Command = TD_CHANGENUM;
  474.   (void) DoIO((struct IORequest *) diskReq);
  475.  
  476.   diskReq->iotd_Req.io_Command = TD_GETGEOMETRY;
  477.   diskReq->iotd_Req.io_Data = &drGeom;
  478.   (void) DoIO((struct IORequest *) diskReq);
  479.  
  480.   cylSize = drGeom.dg_TrackSectors * drGeom.dg_SectorSize;
  481.   GetBuffer(cylSize);
  482.   total = drGeom.dg_TotalSectors * drGeom.dg_SectorSize;
  483.   total >>= 10;
  484.   OutLine(FALSE,
  485.           "Change number for drive %s (%ld KBytes) is %d;",
  486.           driveName, total,
  487.           (diskChangeCount = diskReq->iotd_Req.io_Actual));
  488.  
  489. #ifdef DT_DEBUG
  490.   OutLine(FALSE, "%ld heads, %ld cylinders",
  491.           drGeom.dg_Heads, drGeom.dg_Cylinders);
  492.   OutLine(FALSE, "%ld sec/cyl, %ld sec/trk, %ld bytes/sec",
  493.           drGeom.dg_CylSectors, drGeom.dg_TrackSectors,
  494.           drGeom.dg_SectorSize);
  495.   OutLine(FALSE, "%ld total sectors, %ld bytes/trk",
  496.           drGeom.dg_TotalSectors, cylSize);
  497. #endif
  498.  
  499. /**
  500.  | Pass 1
  501.  | Seek over the floppy.
  502. **/
  503.  
  504.   Motor(ON);
  505.   SeekFullRange(1);
  506.   if (nErFil) {
  507.     Motor(OFF);
  508.     OutLine(FALSE, "* %d hard error%s detected seeking drive %s",
  509.             nErFil, (nErFil == 1 ? "" : "s"), driveName);
  510.     return;
  511.   } else {
  512.     OutLine(FALSE, "  no errors detected seeking over full disk range.");
  513.   }
  514.  
  515. /**
  516.  | Pass 2
  517.  | Read all disk tracks.
  518. **/
  519.  
  520.   OutLine(FALSE, "Checking all disk tracks:");
  521.   for (cyl=0; cyl<(int)drGeom.dg_Cylinders; cyl++) {
  522.     for (head=0; head<(int)drGeom.dg_Heads; head++) {
  523.       OutLine(TRUE,
  524.               "  reading cylinder %d, head %d ...", cyl, head);
  525.       if (CheckBreak()) {
  526.         Motor(OFF);
  527.         return;
  528.       }
  529.  
  530.       ReadCyl(cyl, head);
  531.       if ((error = diskReq->iotd_Req.io_Error) != 0) {
  532.         OutLine(FALSE, "* Error 0x%X detected for cylinder %d, head %d",
  533.             error, cyl, head);
  534.         nErFil++;
  535.       }
  536.     }
  537.   }
  538.   Motor(OFF);
  539.  
  540.   if (nErFil) {
  541.     OutLine(FALSE, "* %d hard error%s detected reading drive %s.",
  542.         nErFil, (nErFil == 1 ? "" : "s"), driveName);
  543.     return;
  544.   } else {
  545.     OutLine(FALSE, "  no errors detected reading drive %s.", driveName);
  546.   }
  547.  
  548. /**
  549.  | Pass 3
  550.  | File integrity check.
  551. **/
  552.  
  553.   OutLine(FALSE, "Checking all files in drive %s", driveName);
  554.   CheckDir(driveName, TRUE);
  555. }
  556.  
  557. static void EndTest(void)
  558. {
  559. /**
  560.  | This procedure outputs some statistics on the test;
  561.  | and frees all unneeded resources. The buffer only is
  562.  | retained, just in case we want to check some other thing...
  563. **/
  564.  
  565.   if (pBWind != NULL) {
  566.     struct IntuiMessage *pIM;
  567.  
  568.     while ((pIM = GT_GetIMsg(pBWind->UserPort)) != NULL) {
  569.       GT_ReplyIMsg(pIM);
  570.     }
  571.     CloseWindow(pBWind);
  572.     pBWind = NULL;
  573.   }
  574.  
  575.   if (diskReq != NULL) {
  576.     CloseDevice((struct IORequest *) diskReq);
  577.     DeleteExtIO((struct IORequest *) diskReq);
  578.     diskReq = NULL;
  579.   }
  580.  
  581.   if (diskPort != NULL) {
  582.     DeletePort(diskPort);
  583.     diskPort = NULL;
  584.   }
  585.  
  586.   if (nDirs + nFiles)
  587.     OutLine(FALSE,
  588.             "%d director%s and %d file%s checked: %d error%s detected.",
  589.             nDirs,  (nDirs  == 1 ? "y" : "ies"),
  590.             nFiles, (nFiles == 1 ? ""  : "s"),
  591.             nErFil, (nErFil == 1 ? ""  : "s"));
  592.  
  593.   BusyState(FALSE);
  594. }
  595.  
  596. static void FileStuff(
  597.   BPTR dirLock,
  598.   char *dirName,
  599.   char *filName,
  600.   int i
  601. ){
  602. /**
  603.  | The driver for the integrity test on a single file.
  604.  | We allocate a buffer and clear the scroller; then CD to
  605.  | the destination directory, call CheckFile in order to
  606.  | perform the actual test, and CD back.
  607.  | If dirName is NULL or contains the NULL string, the directory
  608.  | name is read from dirLock.
  609.  | If i is zero, the scroller is cleared.
  610. **/
  611.  
  612.   BPTR oldLock;
  613.   char name[FILENAME_MAX];
  614.  
  615.   GetBuffer(FILBUF_DIM);
  616.   StartTest(i);
  617.   OutLine(FALSE, "File integrity check ...");
  618.   oldLock = CurrentDir(dirLock);
  619.  
  620.   if (dirName == NULL   ||   *dirName == '\0') {
  621.     (void) NameFromLock(dirLock, (dirName = name), PATHNAME_MAX);
  622.   }
  623.   OutLine(FALSE, "  Sitting in directory %s ...", dirName);
  624.  
  625.   CheckFile(filName);
  626.   (void) CurrentDir(oldLock);
  627.   EndTest();
  628. }
  629.  
  630. static void GetBuffer(
  631.   const ULONG size
  632. ){
  633. /**
  634.  | If no buffer has been previously allocated, GetBuffer() queries
  635.  | for memory from the heap; if the previously allocated buffer is
  636.  | too small, it is released and reallocated.
  637.  | N.B.: we ask for *CHIP* memory, because the buffer has to be used
  638.  |       for disk reading too; for file reading, MEMF_PUBLIC should
  639.  |       be enough.
  640. **/
  641.  
  642.   if (diskBuffer != NULL) {
  643.     if (diskBufferLength >= size) return;
  644.     FreeMem(diskBuffer, diskBufferLength);
  645.   }
  646.  
  647.   if ((diskBuffer = AllocMem(size, MEMF_CHIP)) == NULL) {
  648.     Error("Error from AllocMem");
  649.   }
  650.   diskBufferLength = size;
  651. }
  652.  
  653. static void Motor(
  654.   const ULONG action
  655. ){
  656. /**
  657.  | Issues to the drive a MOTOR ON or OFF request; "action"
  658.  | is the preprocessor symbol ON or OFF.
  659. **/
  660.  
  661.   diskReq->iotd_Req.io_Length  = action;
  662.   diskReq->iotd_Req.io_Command = TD_MOTOR;
  663.   (void) DoIO((struct IORequest *) diskReq);
  664. }
  665.  
  666. static void OutLine(
  667.   const BOOL last,
  668.   char *fmt,
  669.   ...
  670. ){
  671. /**
  672.  | Formatted output on the screen; if "last" is non zero, the
  673.  | "special mode" for the output to be discarded is used.
  674.  | Currently only the track test calls OutLine() with last=TRUE.
  675. **/
  676.  
  677.   va_list vl;
  678.   size_t n;
  679.  
  680.   va_start(vl, fmt);
  681.   n = (size_t) vsprintf(slate, fmt, vl);
  682.   va_end(vl);
  683.  
  684.   if (last) {
  685.     LastLine(slate, n);
  686.   } else {
  687.     AddLine(slate, n, TRUE);
  688.   }
  689. }
  690.  
  691. static void ReadCyl(
  692.   const int cyl,
  693.   const int hd
  694. ){
  695. /**
  696.  | Issues the command to read a whole cylinder on the
  697.  | internal buffer.
  698. **/
  699.  
  700.   diskReq->iotd_Req.io_Length   = cylSize;
  701.   diskReq->iotd_Req.io_Data     = (APTR) diskBuffer;
  702.   diskReq->iotd_Req.io_Command  = ETD_READ;
  703.   diskReq->iotd_Count           = diskChangeCount;
  704.   diskReq->iotd_Req.io_Offset   =
  705.     drGeom.dg_SectorSize * (drGeom.dg_TrackSectors *
  706.       ((ULONG) hd + drGeom.dg_Heads * (ULONG) cyl));
  707.  
  708.   (void) DoIO((struct IORequest *) diskReq);
  709. }
  710.  
  711. static void SeekFullRange(
  712.   const SHORT howmany
  713. ){
  714. /**
  715.  | Performs "howmany" seeks first to the highest
  716.  | cylinder of the drive, and then to the lowest.
  717. **/
  718.  
  719.   int i;
  720.   SHORT error;
  721.  
  722.   for (i=1; i<=howmany; i++) {
  723.     diskReq->iotd_Req.io_Offset =
  724.       ((drGeom.dg_Cylinders - 1) * drGeom.dg_TrackSectors *
  725.         drGeom.dg_Heads - 1) * drGeom.dg_SectorSize;
  726.     diskReq->iotd_Req.io_Command = TD_SEEK;
  727.     (void) DoIO((struct IORequest *) diskReq);
  728.     if ((error = diskReq -> iotd_Req.io_Error) != 0) {
  729.       OutLine(FALSE, "* Seek cycle %d, error 0x%X ...", i, error);
  730.       nErFil++;
  731.     }
  732.  
  733.     diskReq->iotd_Req.io_Offset = 0;
  734.     diskReq->iotd_Req.io_Command = TD_SEEK;
  735.     (void) DoIO((struct IORequest *) diskReq);
  736.     if ((error = diskReq->iotd_Req.io_Error) != 0) {
  737.       OutLine(FALSE, "* Seek cycle %d, error 0x%X ...", i, error);
  738.       nErFil++;
  739.     }
  740.   }
  741. }
  742.  
  743. static void StartTest(
  744.   int i
  745. ){
  746. /**
  747.  | Zeroes the statistics variables, the break flag,
  748.  | and clears the output window. Then creates the
  749.  | "break" window.
  750.  | If i is zero, the scroller is cleared.
  751. **/
  752.  
  753.   nDirs = nFiles = nErFil = 0;
  754.   abortDT = 0;
  755.   if (!i) ClearText(TRUE);
  756.   BusyState(TRUE);
  757.  
  758.   if ((pBWind = OpenWindowTags(NULL,
  759.                 WA_Left,         pWind->LeftEdge + bkwLeft,
  760.                 WA_Top,          pWind->TopEdge  + bkwTop,
  761.                 WA_Width,        bkwWidth,
  762.                 WA_Height,       bkwHeight,
  763.                 WA_Activate,     TRUE,
  764.                 WA_DragBar,      TRUE,
  765.                 WA_DepthGadget,  TRUE,
  766.                 WA_SmartRefresh, TRUE,
  767.                 WA_IDCMP,        BKW_IDCMP,
  768.                 WA_Title,        PROG_NAME " v" REVISION,
  769.                 WA_Gadgets,      pBGad,
  770.                 WA_PubScreen,    pScr,
  771.                 TAG_DONE)) == NULL) {
  772.     Error("Couldn't open the break window");
  773.   }
  774.   GT_RefreshWindow(pBWind, NULL);
  775. }
  776.